package com.ydlclass.ChannelHandler.handler.InboundHandler;

import com.ydlclass.ServiceConfig;
import com.ydlclass.Transport.message.Request.RequestPayload;
import com.ydlclass.Transport.message.Request.YrpcRequest;
import com.ydlclass.Transport.message.Response.YrpcResponse;
import com.ydlclass.YrpcBootstrap;
import com.ydlclass.core.RateLimiter;
import com.ydlclass.core.TokenBuketRateLimiter;
import com.ydlclass.enumeration.RequestTypeEnum;
import com.ydlclass.enumeration.ResponseCodeEnum;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;

import static com.ydlclass.YrpcBootstrap.SERVERS_LIST;
import static com.ydlclass.YrpcBootstrap.yrpcBootstrap;

/**
 * 服务提供方根据请求去调用方法获取结果
 * @Author duWenJian
 * @Date 2024/2/21 14:43
 * @Version 1.0
 */
@Slf4j
public class MethodCallHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 1、封装基本响应
        YrpcRequest yrpcRequest = (YrpcRequest) msg;
        YrpcResponse yrpcResponse = YrpcResponse.builder()
                .requestId(yrpcRequest.getRequestId())
                .serializeType(yrpcRequest.getSerializeType())
                .compressType(yrpcRequest.getCompressType())
                .timeStamp(yrpcRequest.getTimeStamp())
                .build();

        // 2、完成限流的操作
        SocketAddress socketAddress = ctx.channel().remoteAddress();
        // 如果我们想要针对性的限流，应该一个地址匹配一个限流器
        RateLimiter rateLimiter = YrpcBootstrap.getInstance().getConfiguration().getEVERY_IP_RATELimiter().get(socketAddress);
        if (rateLimiter == null){
            rateLimiter = new TokenBuketRateLimiter(100, 100);
            YrpcBootstrap.getInstance().getConfiguration().getEVERY_IP_RATELimiter().put((InetSocketAddress) socketAddress, rateLimiter);
        }

        // 3、 限流
        if (!rateLimiter.AllocateRequest()){
                yrpcResponse.setResponseCode(ResponseCodeEnum.RATE_LIMIT.getCode());
        } else
        // 4、处理心跳
        if ((yrpcRequest.getRequestType() == RequestTypeEnum.HeartbeatRequest.getId())){
                yrpcResponse.setResponseCode(ResponseCodeEnum.SUCCESS_HEART_BEAT.getCode());
        } else {
        // 5、处理方法调用结果
        // ----------------------------------具体的调用过程-----------------------------------------

        // 1、获取负载内容
        RequestPayload payload = yrpcRequest.getRequestPayload();
        // 2、根据负载内容进行方法调用,获取结果
        try {
            Object target = callTargetMethod(payload);
            // 3、封装响应消息
            // todo
            yrpcResponse.setResponseCode(ResponseCodeEnum.SUCCESS.getCode());
            yrpcResponse.setResponseBody(target);
            if (log.isDebugEnabled()) {
                log.debug("请求:{}已经在服务端完成方法调用,并封装响应结果",yrpcResponse.getRequestId());
            }
        } catch (Exception e) {
            log.error("调用方法{}发生异常",payload.getMethodName());
            yrpcResponse.setResponseCode(ResponseCodeEnum.FAIL.getCode());
        }
    }
        // 6、发送响应消息
        ctx.channel().writeAndFlush(yrpcResponse);

    }

    private Object callTargetMethod(RequestPayload payload) {
        String interfaceName = payload.getInterfaceName();
        String methodName = payload.getMethodName();
        Class<?>[] parameterTypes = payload.getParameterTypes();
        Object[] parametersValue = payload.getParametersValue();
        Class<?> returnType = payload.getReturnType();

        // 寻找合适的类进行方法调用,寻找匹配暴露出去的具体实现
        ServiceConfig<?> serviceConfig = SERVERS_LIST.get(interfaceName);
        // 找到方法实现  Hello.class
        Object refImpl = serviceConfig.getRef();
        // 反射调用方法
        Object resultValue = null;
        try {
            Class<?> aClass = refImpl.getClass();
            Method method = aClass.getDeclaredMethod(methodName, parameterTypes);
            resultValue = method.invoke(refImpl, parametersValue);
        } catch (Exception e) {
            log.error("调用服务:{},方法:{},参数:{},通过反射获取对失败,原因是:{}",interfaceName,methodName,parametersValue,e);
            throw new RuntimeException("通过反射获取对失败,原因是:" + e);
        }
        return resultValue;
    }
}
